#!/usr/bin/env bash
# CI Changes Detector
# Analyzes git changes to determine which CI jobs need to run
# Usage: script/ci-changes-detector [base-ref]

set -euo pipefail

BASE_REF="${1:-origin/master}"
CURRENT_REF="${2:-HEAD}"

# Handle initial commits where before SHA is all zeros
if [[ "$BASE_REF" =~ ^0+$ ]]; then
  BASE_REF="origin/master"
fi

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

# Fetch the base ref if it's a remote ref
if [[ "$BASE_REF" == origin/* ]]; then
  if ! git fetch origin master --depth=50 2>/dev/null; then
    echo "${RED}Error: Failed to fetch $BASE_REF${NC}"
    echo "Please check your network connection and try again."
    exit 1
  fi
fi

# Validate that the base ref exists
if ! git rev-parse --verify "$BASE_REF" >/dev/null 2>&1; then
  echo "${RED}Error: Base ref '$BASE_REF' does not exist${NC}"
  echo "Available branches:"
  git branch -a | head -10
  exit 1
fi

# Get list of changed files (committed + uncommitted)
CHANGED_FILES=$(git diff --name-only "$BASE_REF"..."$CURRENT_REF" 2>/dev/null || echo "")

# For local development, also include uncommitted changes
if [ -z "${CI:-}" ] && [ "$CURRENT_REF" = "HEAD" ]; then
  UNCOMMITTED=$(git diff --name-only HEAD 2>/dev/null || echo "")
  UNTRACKED=$(git ls-files --others --exclude-standard 2>/dev/null || echo "")
  CHANGED_FILES=$(printf "%s\n%s\n%s" "$CHANGED_FILES" "$UNCOMMITTED" "$UNTRACKED" | grep -v '^$' || echo "")
fi

if [ -z "$CHANGED_FILES" ]; then
  echo "No changes detected between $BASE_REF and $CURRENT_REF"
  exit 0
fi

# Initialize flags
DOCS_ONLY=true
LINT_CONFIG_CHANGED=false
PRO_LINT_CONFIG_CHANGED=false
RUBY_CHANGED=false
RSPEC_CHANGED=false
SPEC_DUMMY_CHANGED=false
JS_CHANGED=false
GENERATORS_CHANGED=false # needs to be duplicated to pro
PRO_RUBY_CHANGED=false
PRO_RSPEC_CHANGED=false
PRO_JS_CHANGED=false
PRO_DUMMY_CHANGED=false
PRO_NODE_RENDERER_CHANGED=false
UNCATEGORIZED_CHANGED=false

# Analyze each changed file
while IFS= read -r file; do
  case "$file" in
    # Documentation files
    *.md|*.mdx|*.markdown|*.rst|*.txt|docs/*|docs/**/*|react_on_rails_pro/docs/*|react_on_rails_pro/docs/**/*|SUMMARY.md|book.json)
      # Don't change DOCS_ONLY flag, just continue
      ;;

    # Ruby source code
    react_on_rails/lib/*.rb|react_on_rails/lib/**/*.rb|Gemfile|Gemfile.lock|rakelib/run_rspec.rake|rakelib/node_package.rake|rakelib/dummy_apps.rake)
      DOCS_ONLY=false
      RUBY_CHANGED=true
      ;;

    # Ruby gem-specific specs
    react_on_rails/spec/react_on_rails/*|react_on_rails/spec/react_on_rails/**/*|.github/workflows/gem-tests.yml)
      DOCS_ONLY=false
      RSPEC_CHANGED=true
      ;;

    # Generators
    react_on_rails/lib/generators/*|react_on_rails/lib/generators/**/*|rakelib/example_type.rb|rakelib/examples_config.yml|rakelib/shakapacker_examples.rake|.github/workflows/examples.yml)
      DOCS_ONLY=false
      GENERATORS_CHANGED=true
      ;;

    # JavaScript/TypeScript source (including tests, scripts, and package files)
    package.json|yarn.lock|packages/react-on-rails/src/*|packages/react-on-rails/src/**/*|packages/react-on-rails/tests/*|packages/react-on-rails/tests/**/*|packages/react-on-rails/scripts/*|packages/react-on-rails/scripts/**/*|packages/react-on-rails/package.json|packages/react-on-rails/tsconfig.json|.github/workflows/package-js-tests.yml)
      DOCS_ONLY=false
      JS_CHANGED=true
      ;;

    # Dummy app
    react_on_rails/spec/dummy/*|react_on_rails/spec/dummy/**/*|.github/workflows/integration-tests.yml)
      DOCS_ONLY=false
      SPEC_DUMMY_CHANGED=true
      ;;

    # React on Rails Pro source code
    react_on_rails_pro/lib/*|react_on_rails_pro/lib/**/*)
      DOCS_ONLY=false
      PRO_RUBY_CHANGED=true
      ;;

    # JavaScript/TypeScript Pro source (including tests, scripts, and package files)
    packages/react-on-rails-pro/src/*|packages/react-on-rails-pro/src/**/*|packages/react-on-rails-pro/tests/*|packages/react-on-rails-pro/tests/**/*|packages/react-on-rails-pro/scripts/*|packages/react-on-rails-pro/scripts/**/*|packages/react-on-rails-pro/package.json|packages/react-on-rails-pro/tsconfig.json)
      DOCS_ONLY=false
      PRO_JS_CHANGED=true
      ;;

    # Ruby Pro gem-specific specs
    react_on_rails_pro/spec/react_on_rails/*|react_on_rails_pro/spec/react_on_rails/**/*|.github/workflows/pro-gem-tests.yml)
      DOCS_ONLY=false
      PRO_RSPEC_CHANGED=true
      ;;

    # React on Rails Pro package / dummy app
    react_on_rails_pro/spec/dummy/*|react_on_rails_pro/spec/dummy/**/*|.github/workflows/pro-integration-tests.yml)
      DOCS_ONLY=false
      PRO_DUMMY_CHANGED=true
      ;;

    # React on Rails Pro Node Renderer package (JS/TS source, tests, configs)
    packages/react-on-rails-pro-node-renderer/src/*|packages/react-on-rails-pro-node-renderer/src/**/*|packages/react-on-rails-pro-node-renderer/tests/*|packages/react-on-rails-pro-node-renderer/tests/**/*|packages/react-on-rails-pro-node-renderer/scripts/*|packages/react-on-rails-pro-node-renderer/scripts/**/*|packages/react-on-rails-pro-node-renderer/package.json|packages/react-on-rails-pro-node-renderer/tsconfig.json|.github/workflows/node-renderer-tests.yml)
      DOCS_ONLY=false
      PRO_NODE_RENDERER_CHANGED=true
      ;;

    # Lint/format configuration
    .rubocop.yml|.eslintrc*|.prettierrc*|tsconfig.json|.editorconfig|.github/workflows/lint-js-and-ruby.yml)
      DOCS_ONLY=false
      LINT_CONFIG_CHANGED=true
      ;;

    # React on Rails Pro lint/format configuration
    react_on_rails_pro/.rubocop.yml|react_on_rails_pro/.eslintrc*|react_on_rails_pro/.prettierrc*|react_on_rails_pro/tsconfig.json|react_on_rails_pro/.editorconfig|.github/workflows/pro-lint.yml)
      DOCS_ONLY=false
      PRO_LINT_CONFIG_CHANGED=true
      ;;

    # CI infrastructure files (scripts, workflows, CI configs)
    # Changes to CI infrastructure should trigger tests to validate the changes work
    script/*|script/**/*|bin/*|bin/**/*|.github/workflows/*|.github/actions/*|.github/actions/**/*|lefthook.yml)
      DOCS_ONLY=false
      RUBY_CHANGED=true  # Trigger all tests for CI infrastructure changes
      JS_CHANGED=true
      SPEC_DUMMY_CHANGED=true
      GENERATORS_CHANGED=true
      PRO_RUBY_CHANGED=true
      PRO_JS_CHANGED=true
      PRO_DUMMY_CHANGED=true
      ;;

    # Catch-all: any file not explicitly categorized above
    # Philosophy: When in doubt, run everything to ensure safety
    # This prevents new file types or directories from being silently ignored by CI
    *)
      DOCS_ONLY=false
      UNCATEGORIZED_CHANGED=true
      ;;
  esac
done <<< "$CHANGED_FILES"

# Output results
echo "=== CI Changes Analysis ==="
echo "Base: $BASE_REF | Current: $CURRENT_REF"
echo ""

if [ "$DOCS_ONLY" = true ]; then
  # Define all CI flags in one place to avoid duplication
  # Format: "flag_name:value" pairs
  CI_FLAGS=(
    "docs_only:true"
    "run_lint:false"
    "run_ruby_tests:false"
    "run_js_tests:false"
    "run_dummy_tests:false"
    "run_generators:false"
    "run_pro_lint:false"
    "run_pro_tests:false"
    "run_pro_dummy_tests:false"
  )

  # Output to GITHUB_OUTPUT if in GitHub Actions
  if [ -n "${GITHUB_OUTPUT:-}" ]; then
    for flag in "${CI_FLAGS[@]}"; do
      echo "${flag//:/=}" >> "$GITHUB_OUTPUT"
    done
  fi

  # Output as JSON if requested
  if [ "${CI_JSON_OUTPUT:-}" = "1" ]; then
    echo "{"
    for i in "${!CI_FLAGS[@]}"; do
      flag_name="${CI_FLAGS[$i]%%:*}"
      flag_value="${CI_FLAGS[$i]#*:}"
      # Add comma for all but the last item
      if [ "$i" -eq $((${#CI_FLAGS[@]} - 1)) ]; then
        echo "  \"$flag_name\": $flag_value"
      else
        echo "  \"$flag_name\": $flag_value,"
      fi
    done
    echo "}"
  fi

  echo -e "${GREEN}✓ Documentation-only changes${NC}"
  echo ""
  echo "Recommended CI jobs: NONE (skip CI)"
  echo ""
  echo "You can skip CI on this commit with:"
  echo "  git commit --amend -m \"\$(git log -1 --pretty=%B)\" -m \"[skip ci]\""
  exit 0
fi

echo "Changed file categories:"
[ "$RUBY_CHANGED" = true ] && echo -e "${YELLOW}  • Ruby source code${NC}"
[ "$JS_CHANGED" = true ] && echo -e "${YELLOW}  • JavaScript/TypeScript code${NC}"
[ "$RSPEC_CHANGED" = true ] && echo -e "${YELLOW}  • RSpec tests${NC}"
[ "$SPEC_DUMMY_CHANGED" = true ] && echo -e "${YELLOW}  • Dummy app${NC}"
[ "$GENERATORS_CHANGED" = true ] && echo -e "${YELLOW}  • Generators${NC}"
[ "$PRO_JS_CHANGED" = true ] && echo -e "${YELLOW}  • React on Rails Pro JavaScript/TypeScript${NC}"
[ "$PRO_RSPEC_CHANGED" = true ] && echo -e "${YELLOW}  • React on Rails Pro RSpec tests${NC}"
[ "$PRO_RUBY_CHANGED" = true ] && echo -e "${YELLOW}  • React on Rails Pro Ruby source code${NC}"
[ "$PRO_DUMMY_CHANGED" = true ] && echo -e "${YELLOW}  • React on Rails Pro Dummy app${NC}"
[ "$PRO_NODE_RENDERER_CHANGED" = true ] && echo -e "${YELLOW}  • React on Rails Pro Node Renderer package${NC}"
[ "$LINT_CONFIG_CHANGED" = true ] && echo -e "${YELLOW}  • Lint/format configuration${NC}"
[ "$PRO_LINT_CONFIG_CHANGED" = true ] && echo -e "${YELLOW}  • React on Rails Pro lint/format configuration${NC}"
[ "$UNCATEGORIZED_CHANGED" = true ] && echo -e "${YELLOW}  • Uncategorized changes (running full suite for safety)${NC}"

echo ""
echo "Recommended CI jobs:"

# Determine which jobs to run
RUN_LINT=false
RUN_RUBY_TESTS=false
RUN_JS_TESTS=false
RUN_DUMMY_TESTS=false
RUN_GENERATORS=false
RUN_PRO_LINT=false
RUN_PRO_TESTS=false
RUN_PRO_DUMMY_TESTS=false
RUN_PRO_NODE_RENDERER_TESTS=false

if [ "$LINT_CONFIG_CHANGED" = true ] || [ "$RUBY_CHANGED" = true ] || [ "$JS_CHANGED" = true ] || [ "$SPEC_DUMMY_CHANGED" = true ]; then
  RUN_LINT=true
fi

if [ "$RUBY_CHANGED" = true ] || [ "$RSPEC_CHANGED" = true ]; then
  RUN_RUBY_TESTS=true
fi

if [ "$JS_CHANGED" = true ]; then
  RUN_JS_TESTS=true
fi

if [ "$SPEC_DUMMY_CHANGED" = true ] || [ "$RUBY_CHANGED" = true ] || [ "$JS_CHANGED" = true ]; then
  RUN_DUMMY_TESTS=true
fi

if [ "$GENERATORS_CHANGED" = true ]; then
  RUN_GENERATORS=true
fi

if [ "$PRO_LINT_CONFIG_CHANGED" = true ] || [ "$PRO_RUBY_CHANGED" = true ] || [ "$PRO_JS_CHANGED" = true ] || [ "$PRO_DUMMY_CHANGED" = true ] || [ "$RUBY_CHANGED" = true ]; then
  RUN_PRO_LINT=true
fi

if [ "$PRO_RUBY_CHANGED" = true ] || [ "$PRO_RSPEC_CHANGED" = true ] || [ "$PRO_JS_CHANGED" = true ] || [ "$RUBY_CHANGED" = true ]; then
  RUN_PRO_TESTS=true
fi

if [ "$PRO_DUMMY_CHANGED" = true ] || [ "$PRO_RUBY_CHANGED" = true ] || [ "$PRO_JS_CHANGED" = true ] || [ "$RUBY_CHANGED" = true ]; then
  RUN_PRO_DUMMY_TESTS=true
fi

if [ "$PRO_NODE_RENDERER_CHANGED" = true ] || [ "$PRO_JS_CHANGED" = true ] || [ "$RUBY_CHANGED" = true ]; then
  RUN_PRO_NODE_RENDERER_TESTS=true
fi

# If we encounter a change that isn't explicitly categorized, default to running everything
if [ "$UNCATEGORIZED_CHANGED" = true ]; then
  RUN_LINT=true
  RUN_RUBY_TESTS=true
  RUN_JS_TESTS=true
  RUN_DUMMY_TESTS=true
  RUN_GENERATORS=true
  RUN_PRO_LINT=true
  RUN_PRO_TESTS=true
  RUN_PRO_DUMMY_TESTS=true
  RUN_PRO_NODE_RENDERER_TESTS=true
fi

[ "$RUN_LINT" = true ] && echo "  ✓ Lint (Ruby + JS)"
[ "$RUN_RUBY_TESTS" = true ] && echo "  ✓ RSpec gem tests"
[ "$RUN_JS_TESTS" = true ] && echo "  ✓ JS unit tests"
[ "$RUN_DUMMY_TESTS" = true ] && echo "  ✓ Dummy app integration tests"
[ "$RUN_GENERATORS" = true ] && echo "  ✓ Generator tests"
[ "$RUN_PRO_LINT" = true ] && echo "  ✓ React on Rails Pro Lint (Ruby + JS)"
[ "$RUN_PRO_TESTS" = true ] && echo "  ✓ React on Rails Pro RSpec unit tests (Ruby + JS)"
[ "$RUN_PRO_DUMMY_TESTS" = true ] && echo "  ✓ React on Rails Pro Dummy app integration tests"
[ "$RUN_PRO_NODE_RENDERER_TESTS" = true ] && echo "  ✓ React on Rails Pro Node Renderer tests"

# Export as GitHub Actions outputs if running in CI
if [ -n "${GITHUB_OUTPUT:-}" ]; then
  {
    echo "docs_only=$DOCS_ONLY"
    echo "run_lint=$RUN_LINT"
    echo "run_ruby_tests=$RUN_RUBY_TESTS"
    echo "run_js_tests=$RUN_JS_TESTS"
    echo "run_dummy_tests=$RUN_DUMMY_TESTS"
    echo "run_generators=$RUN_GENERATORS"
    echo "run_pro_lint=$RUN_PRO_LINT"
    echo "run_pro_tests=$RUN_PRO_TESTS"
    echo "run_pro_dummy_tests=$RUN_PRO_DUMMY_TESTS"
    echo "run_pro_node_renderer_tests=$RUN_PRO_NODE_RENDERER_TESTS"
  } >> "$GITHUB_OUTPUT"
fi

# Export as JSON for programmatic parsing (when CI_JSON_OUTPUT=1)
if [ "${CI_JSON_OUTPUT:-}" = "1" ]; then
  cat << EOF
{
  "docs_only": $DOCS_ONLY,
  "run_lint": $RUN_LINT,
  "run_ruby_tests": $RUN_RUBY_TESTS,
  "run_js_tests": $RUN_JS_TESTS,
  "run_dummy_tests": $RUN_DUMMY_TESTS,
  "run_generators": $RUN_GENERATORS,
  "run_pro_lint": $RUN_PRO_LINT,
  "run_pro_tests": $RUN_PRO_TESTS,
  "run_pro_dummy_tests": $RUN_PRO_DUMMY_TESTS,
  "run_pro_node_renderer_tests": $RUN_PRO_NODE_RENDERER_TESTS
}
EOF
fi
